package personal.zq.practice.common.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

/**
 * excel导入导出工具
 *
 * @author 尚气(zhao tian hao)
 * @version 1.0
 * @date 2020/2/26 22:13
 */
@Slf4j
@Component
public class EasyExcelUtils {

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 有模板对象的写excel
     *
     * @param dataList 数据
     * @param clazz    数据类型
     * @return byte[]
     */
    public byte[] writeExcel(List dataList, Class clazz) {
        ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
        writeExcel(out, dataList, clazz);
        byte[] bytes = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            log.error("异常", e);
        }
        return bytes;
    }

    /**
     * 有模板对象的读excel
     *
     * @param in                输入流
     * @param sheetNo           读取哪个sheet
     * @param dataClazz         要转换成的类型
     * @param excelDataListener 此参数必须是new出来的
     */
    public <T> void readExcel(InputStream in, Class dataClazz, Integer sheetNo, ExcelDataListener<T> excelDataListener) {
        applicationContext.getAutowireCapableBeanFactory().autowireBean(excelDataListener);
        EasyExcel.read(in, dataClazz, excelDataListener)
                .sheet(sheetNo)
                .doReadSync();
    }

    /**
     * excel上传模板导出(只有excel表头)
     *
     * @param headNameList excel头的字符串列表
     */
    public byte[] exportTemplate(List<String> headNameList) {
        List<List<String>> headList = getHeadList(headNameList);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        writeExcelWithNoModel(out, Arrays.asList(Arrays.asList("")), headList);
        byte[] bytes = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            log.error("异常", e);
        }
        return bytes;
    }

    /**
     * 无模板对象的写excel
     *
     * @param dataList     内层的一个list是一行数据
     * @param headNameList 表头字符串
     * @return byte[]
     */
    public byte[] writeExcelWithNoModel(List<List<Object>> dataList, List<String> headNameList) {
        List<List<String>> headList = getHeadList(headNameList);
        ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
        writeExcelWithNoModel(out, dataList, headList);
        byte[] bytes = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            log.error("异常", e);
        }
        return bytes;
    }

    /**
     * 定制书写
     */
    public byte[] styleModel(List<List<Object>> dataList, List<String> headNameList) {
        List<List<String>> headList = getHeadList(headNameList);
        ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
        styleWrite(out, dataList, headList);
        byte[] bytes = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            log.error("异常", e);
        }
        return bytes;
    }

    /**
     * 无模板对象的读excel
     *
     * @param in           输入流
     * @param sheetNo      读取哪个sheet
     * @param dataListener 此参数必须是new出来的
     */
    public void readExcelWithNoModel(InputStream in, Integer sheetNo
            , NoModelDataListener dataListener) {
        applicationContext.getAutowireCapableBeanFactory().autowireBean(dataListener);
        EasyExcel.read(in, dataListener).sheet(sheetNo).doRead();
    }

    /**
     * 以下四个方法是一套的, 用于写入多个sheet, 每个sheet可以写不同的对象 <br>
     * 1.先获取writer <br>
     * 2.多次调用writeToSheet写入不同sheet <br>
     * 3.最后调finish关闭 <br>
     */
    public ExcelWriter getWriter(OutputStream out) {
        return EasyExcel.write(out).build();
    }

    /**
     * 有模板对象的写
     */
    public EasyExcelUtils writeToSheet(List dataList, Class clazz, ExcelWriter writer, Integer sheetNo) {
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo)
                .head(clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .build();
        writer.write(dataList, writeSheet);
        return this;
    }

    /**
     * 无模板对象的写
     */
    public EasyExcelUtils writeToSheetWithNoModel(List<List<Object>> dataList, List<String> headNameList, ExcelWriter writer, Integer sheetNo) {
        List<List<String>> headList = getHeadList(headNameList);
        WriteSheet writeSheet = EasyExcel.writerSheet("Sheet" + sheetNo)
                .head(headList)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .build();
        writer.write(dataList, writeSheet);
        return this;
    }

    public void finish(ExcelWriter excelWriter) {
        excelWriter.finish();
    }

    /**
     * @param out      写入位置
     * @param dataList 数据
     * @param clazz    数据类型
     * @throws ClassCastException
     */
    private void writeExcel(OutputStream out, List dataList, Class clazz) throws ClassCastException {
        EasyExcel.write(out, clazz)
                .excelType(ExcelTypeEnum.XLSX)
                .sheet("Sheet0")
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(dataList);
    }

    private void writeExcelWithNoModel(OutputStream out, List<List<Object>> dataList, List<List<String>> headList) {
        EasyExcel.write(out)
                .excelType(ExcelTypeEnum.XLSX)
                .sheet("Sheet0")
                .head(headList)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(dataList);
    }

    public void styleWrite(OutputStream out, List<List<Object>> dataList, List<List<String>> headList) {

        EasyExcel.write(out)
                .excelType(ExcelTypeEnum.XLSX)
                .sheet("Sheet0")
                .head(headList)
                .registerWriteHandler(new BizMergeStrategy(headList.size()))
                .doWrite(dataList);
    }

    private List<List<String>> getHeadList(List<String> headNameList) {
        List<List<String>> headList = new ArrayList<>(headNameList.size());
        List<String> head;
        for (String headName : headNameList) {
            head = new ArrayList();
            head.add(headName);
            headList.add(head);
        }
        return headList;
    }
}
